// Application.cpp : implementation file
//

#include "stdafx.h"
#include "ansieditor.h"
#include "Application.h"

#include <initguid.h> // Realizes CLSID definitions
#include "msscript.h" // defines CLSIDs for VBScript and JScript
#include <activscp.h> // Active Scripting Interfaces

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Application

IMPLEMENT_DYNCREATE(Application, CCmdTargetPlus)
IMPLEMENT_OLETYPELIB(Application, LIBID_ANSIEDITOR, _wVerMajor, _wVerMinor)

BEGIN_INTERFACE_MAP(Application, CCmdTargetPlus)
	INTERFACE_PART(Application, IID_IActiveScriptSite, ActiveScriptSite)
	INTERFACE_PART(Application, IID_IActiveScriptSiteWindow, ActiveScriptSiteWindow)
	INTERFACE_PART(Application, IID_IApplication, Dispatch)
END_INTERFACE_MAP()

BEGIN_DISPATCH_MAP(Application, CCmdTargetPlus)
	//{{AFX_DISPATCH_MAP(Application)
	DISP_PROPERTY_EX(Application, "Version", GetVersion, SetNotSupported, VT_BSTR)
	DISP_PROPERTY_EX(Application, "Window", GetWindow, SetNotSupported, VT_DISPATCH)
	DISP_PROPERTY_EX(Application, "Path", GetPath, SetNotSupported, VT_BSTR)
	//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

BEGIN_EVENT_MAP(Application, CCmdTargetPlus)
	//{{AFX_EVENT_MAP(Application)
	EVENT_CUSTOM("OnEventTest", FireOnEventTest, VTS_NONE)
	EVENT_CUSTOM("OnQuit", FireOnQuit, VTS_NONE)
	//}}AFX_EVENT_MAP
END_EVENT_MAP()

//////////////////////////////////////////////////////////////////////////
// Scripting support
/////////////////////////////////////////////////////////////////////////////
// IActiveScriptSite Implementation

STDMETHODIMP_(ULONG)Application::XActiveScriptSite::AddRef()
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)
    return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG)Application::XActiveScriptSite::Release()
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)
    return pThis->ExternalRelease();
}

STDMETHODIMP Application::XActiveScriptSite::QueryInterface(
    REFIID iid, void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP Application::XActiveScriptSite::GetLCID(LCID* plcid)
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)

	return E_NOTIMPL;
}

STDMETHODIMP Application::XActiveScriptSite::GetItemInfo(
            /* [in] */ LPCOLESTR pstrName,
            /* [in] */ DWORD dwReturnMask,
            /* [out] */LPUNKNOWN* ppiunkItem,
            /* [out] */LPTYPEINFO* ppti)
{
	HRESULT hr = S_OK; 

    METHOD_PROLOGUE(Application, ActiveScriptSite)
	USES_CONVERSION;

	TRACE("GetItemInfo: Name = %s Mask = %x\n", OLE2CT(pstrName), dwReturnMask);

	if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
	{
		if (!ppti)
			return E_INVALIDARG;
		*ppti = NULL;
	}

	if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
	{
		if (!ppiunkItem)
			return E_INVALIDARG;
		*ppiunkItem = NULL;
	}

	// Global object
	if (!_wcsicmp(L"Application", pstrName))
	{
		if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
		{
			pThis->GetTypeInfoOfGuid(0 /* lcid unknown! */, CLSID_Application, ppti);
			(*ppti)->AddRef();      // because returning
		}
		
		if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
		{
			*ppiunkItem = pThis->GetIDispatch(TRUE);
			(*ppiunkItem)->AddRef();    // because returning
		}
		return S_OK;
	}

	// Check map for named item
	CCmdTarget* pdispimpl = NULL;
	if (!(pThis->mapNamedItems.Lookup(OLE2CT(pstrName), (CObject*&) pdispimpl)))
	{
		return TYPE_E_ELEMENTNOTFOUND;
	}

	LPUNKNOWN pUnk = NULL;
	ASSERT(NULL != pdispimpl);
	if (pdispimpl->IsKindOf(RUNTIME_CLASS(CCmdTargetPlus)))
		// Automation class
		pUnk = pdispimpl->GetIDispatch(TRUE);
	else
		// Wrapper Class probably
		pUnk = ((CWnd*)pdispimpl)->GetControlUnknown();

	if (NULL == pUnk)
	{
		TRACE("No IUnknown for item!\n");
		// This shouldn't ever happen, since we're providing the items
		return E_FAIL;
	}

	if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
	{
		// Use IProvideClassInfo to get ITypeInfo of coclass!
		IProvideClassInfo *pci = NULL;
		hr = pUnk->QueryInterface(IID_IProvideClassInfo, (void**)&pci);
		if (SUCCEEDED(hr) && pci)
			hr = pci->GetClassInfo(ppti);
		if (FAILED(hr))
			return E_FAIL;
	}
	
	if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
	{
		*ppiunkItem = pUnk;
		(*ppiunkItem)->AddRef();    // because returning
	}

	return S_OK;
}

STDMETHODIMP Application::XActiveScriptSite::GetDocVersionString(LPBSTR pbstrVersion)
{
	METHOD_PROLOGUE(Application, ActiveScriptSite)

	return E_NOTIMPL;

}

STDMETHODIMP Application::XActiveScriptSite::OnScriptTerminate( 
            /* [in] */ const VARIANT* pvarResult,
            /* [in] */ const EXCEPINFO* pexcepinfo)
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)

	return S_OK;

}

STDMETHODIMP Application::XActiveScriptSite::OnStateChange( 
            /* [in] */ SCRIPTSTATE ssScriptState)
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)

	return S_OK;
}

STDMETHODIMP Application::XActiveScriptSite::OnScriptError( 
            /* [in] */ IActiveScriptError* pse)
{
	METHOD_PROLOGUE_EX(Application, ActiveScriptSite)

	EXCEPINFO ei;
	DWORD     dwSrcContext;
	ULONG     ulLine;
	LONG      ichError;
	BSTR      bstrLine = NULL;
	CString strError;

	pse->GetExceptionInfo(&ei);
	pse->GetSourcePosition(&dwSrcContext, &ulLine, &ichError);
	pse->GetSourceLineText(&bstrLine);
	
	CString desc;
	CString src;

	desc = (LPCWSTR)ei.bstrDescription;
	src = (LPCWSTR)ei.bstrSource;

	strError.Format("%s\nSrc: %s\nLine:%d Error:%d Scode:%x", desc, src, ulLine, (int)ei.wCode, ei.scode);
	AfxMessageBox(strError);

	TRACE(strError);
	TRACE("\n");

	return S_OK;
}

STDMETHODIMP Application::XActiveScriptSite::OnEnterScript()
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)
	TRACE("OnEnterScript\n");

	return S_OK;
}

STDMETHODIMP Application::XActiveScriptSite::OnLeaveScript()
{
    METHOD_PROLOGUE(Application, ActiveScriptSite)
	TRACE("OnLeaveScript\n");

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// IActiveScriptSiteWindow Implementation

STDMETHODIMP_(ULONG) Application::XActiveScriptSiteWindow::AddRef()
{
    METHOD_PROLOGUE_EX_(Application, ActiveScriptSiteWindow)
    return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) Application::XActiveScriptSiteWindow::Release()
{
    METHOD_PROLOGUE_EX_(Application, ActiveScriptSiteWindow)
    return pThis->ExternalRelease();
}

STDMETHODIMP Application::XActiveScriptSiteWindow::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
    METHOD_PROLOGUE_EX_(Application, ActiveScriptSiteWindow)
    return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP Application::XActiveScriptSiteWindow::EnableModeless(BOOL fEnable)
{
    METHOD_PROLOGUE_EX_(Application, ActiveScriptSiteWindow)
	
	CWinApp* pApp = AfxGetApp();
	if (!pApp)
		return E_FAIL;

	pApp->EnableModeless(fEnable);
	return S_OK;
}

STDMETHODIMP Application::XActiveScriptSiteWindow::GetWindow(HWND* phWnd)
{
    METHOD_PROLOGUE_EX_(Application, ActiveScriptSiteWindow)

	CWnd* pWnd = AfxGetMainWnd();
	if (!pWnd)
		return E_FAIL;

	*phWnd = pWnd->GetSafeHwnd();
	if (*phWnd)
		return S_OK;
	else
		return E_FAIL;
}

//////////////////////////////////////////////////////////////////////////

BEGIN_MESSAGE_MAP(Application, CCmdTargetPlus)
	//{{AFX_MSG_MAP(Application)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

Application::Application()
{
	EnableAutomation();
	m_pScriptEngine = NULL;
	m_pScriptParser = NULL;
	m_piidEvents = &IID_IApplicationEvents;
	m_piidPrimary = &IID_IApplication;
}

Application::~Application()
{
//	StopEngine();
//	if (m_pScriptParser)
//		m_pScriptParser->Release();
//	if (m_pScriptEngine)
//	{
//		m_pScriptEngine->Close();
//		m_pScriptEngine->Release();
//	}
}


void Application::OnFinalRelease()
{
	// When the last reference for an automation object is released
	// OnFinalRelease is called.  The base class will automatically
	// deletes the object.  Add additional cleanup required for your
	// object before calling the base class.

	StopEngine();
//	CCmdTargetPlus::OnFinalRelease();
}

/////////////////////////////////////////////////////////////////////////////
// Application message handlers

BSTR Application::GetVersion() 
{
	CString strResult;
	strResult = gszProfileName;
	return strResult.AllocSysString();
}

BOOL Application::RunScript(LPCTSTR szScriptText, enumScriptType scriptType)
{
	StopEngine();
	if(!StartEngine(scriptType))
	{
		ShowError(gpApp->m_mui.LoadString(IDS_LOADSCRIPTENGINE_FAILED));
		return FALSE;
	}

	_bstr_t bsScript(szScriptText);
	EXCEPINFO ei;
	m_pScriptParser->ParseScriptText(bsScript, L"Application", NULL, NULL, 0, 0, 0L, NULL, &ei);
//	m_pScriptParser->Release();
	HRESULT hr = m_pScriptEngine->SetScriptState(SCRIPTSTATE_CONNECTED);
	if(FAILED(hr))
	{
		ShowError("SetScriptState to CONNECTED failed\n");
	}
	return TRUE;
}

void Application::StopEngine()
{
	if(m_pScriptParser)
	{
		m_pScriptParser->Release();
		m_pScriptParser = NULL;
	}
	if(m_pScriptEngine)
	{
		HRESULT hr = m_pScriptEngine->Close();
		if(FAILED(hr))
		{
			ShowError(gpApp->m_mui.LoadString(IDS_CLOSESCRIPTENGINE_FAILED));
		}
		m_pScriptEngine->Release();
		m_pScriptEngine = NULL;
	}
	m_bScriptIsRunning = FALSE;
}

BOOL Application::StartEngine(enumScriptType scriptType)
{
	HRESULT hr;

	const CLSID *pclsidEngine = NULL;
	switch(scriptType)
	{
	case ScriptType_VBScript:
		pclsidEngine = &(CLSID_VBScript);
		break;
	case ScriptType_JScript:
		pclsidEngine = &CLSID_JScript;
		break;
	}
	hr = CoCreateInstance(*pclsidEngine, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void **)&m_pScriptEngine);
	if(FAILED(hr))
	{
		return FALSE;
	}
	hr = m_pScriptEngine->QueryInterface(IID_IActiveScriptParse, (void **)&m_pScriptParser);
	if(FAILED(hr))
	{
		goto Failure;
	}
	hr = m_pScriptEngine->SetScriptSite(&m_xActiveScriptSite); // Our MFC-OLE interface implementation
	if(FAILED(hr))
	{
		goto Failure;
	}

	hr = m_pScriptEngine->AddTypeLib(LIBID_ANSIEDITOR, _wVerMajor, _wVerMinor, 0);
	if(FAILED(hr))
	{
		if(hr == TYPE_E_CANTLOADLIBRARY)
		{
			ShowError("A");
		}
		else if(hr == E_UNEXPECTED )
		{
			ShowError("b");
		}
		else
		{
			ShowError("c");
		}
	}

	hr = m_pScriptParser->InitNew();
	if (FAILED(hr))
	{
		goto Failure;
	}

	mapNamedItems[_T("Application")] = this;
	hr = m_pScriptEngine->AddNamedItem(L"Application", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS);
	if (FAILED(hr))
	{
		ShowError("Couldn't add named item Scripter\n");
	}

	mapNamedItems[_T("Window")] = &m_xWindow;
	hr = m_pScriptEngine->AddNamedItem(L"Window", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
	if (FAILED(hr))
	{
		ShowError("Couldn't add named item AButton\n");
	}

	m_bScriptIsRunning = TRUE;
	return TRUE;

Failure:
	if (m_pScriptEngine)
	{
		m_pScriptEngine->Release();
	}
	if (m_pScriptParser)
	{
		m_pScriptParser->Release();
	}
	ShowError("failed to start script engine");
	return FALSE;
}

LPDISPATCH Application::GetWindow() 
{
	return m_xWindow.GetIDispatch(TRUE);
	return NULL;
}

BSTR Application::GetPath() 
{
	return gpApp->GetAppPath().AllocSysString();
}
